import java.util.Random;


public class NNTestEntrance {
	public static int trainTimes = 10000;
	public static double learnRate = 0.9;
	
	public static void main(String[] args){
//		double[] input = {1.0, 0.0, 1.0};
//		network.forword(input);
//		double[] output = {1.0};
//		network.backpropagation(output);
//		network.printNetwork();
		
		Data trainData = new Data("data\\glass.txt");
		Normalizer normalizer = new Normalizer();
		
	//	normalizer.normalizeByMinMax(trainData.dataset, true);
		normalizer.normalizeByZScore(trainData.dataset, true);
	//	normalizer.normalizeByLogistic(trainData.dataset, true);
		
		Data testData = new Data("data\\glass.t");
		
	//		normalizer.normalizeByMinMax(testData.dataset, false);
		normalizer.normalizeByZScore(testData.dataset, false);
		//	normalizer.normalizeByLogistic(testData.dataset, false);
		
	//	Network network = GABPApproach(trainData, testData);
	//	Network network = PSOBPApproach(trainData, testData);
		Network network = SABPApproach(trainData, testData);
	//	Network network = BPApproach(trainData, testData);
		
//		System.out.println(trainData.calMisRecord(network));
//
		System.out.println(testData.calMisRecord(network));



	}
	
	public static Network GABPApproach(Data trainData, Data testData){
		System.out.println("GA-BP Approach!!!");
		Network network = new GeneticAlgorithm(8, 17, 1, 0.9, trainData).run();
		if(network == null)
		{
			network = new Network(8, 17, 1);
			System.out.println("Genetic algorithm does not find the value");
		}
		
		network.setLearningRate(0.9);
		network.setClassNumber(2);
		network.printNetwork();
		System.out.println(trainData.calMisRecord(network));
		System.out.println("!!!!!!!!!");
		Random seed = new Random();
		for (int i = 0; i < trainTimes; i++){
			network.setLearningRate(Math.pow(Math.E, (double)i / trainTimes * -1) * learnRate);
//				if((i % 1000 == 0))
//				{
//					System.out.println(trainData.calMisRecord(network));
//				//	break;
//				}
				
			int index = Math.abs(seed.nextInt()) % trainData.dataset.size();
		//	int index = i % trainData.dataset.size();
			double[] input = new double[trainData.attributeNum - 2];
			for (int j = 0; j < trainData.attributeNum - 2; j++){
				input[j] = trainData.dataset.get(index)[j+2];
			}
			network.forword(input);
			double[] output = network.getOutputFromNumber(trainData.dataset.get(index)[1]);
			network.backpropagation(output);
		}	
		return network;
	}
	
	public static Network BPApproach(Data trainData, Data testData){
		System.out.println("BP Approach!!!");
		Network network = new Network(8, 17, 1);
		network.setLearningRate(learnRate);
		network.setClassNumber(2);
	//	System.out.println(trainData.calMisRecord(network));
		Random seed = new Random();

		for (int i = 0; i < trainTimes; i++){
			if ((i % 1000 == 0)){
				System.out.println(trainData.calMisRecord(network));
			}
			network.setLearningRate(Math.pow(Math.E, (double)i / trainTimes * -1) * learnRate);
			int index = Math.abs(seed.nextInt() % trainData.dataset.size());
		//	int index = i % trainData.dataset.size();
			double[] input = new double[trainData.attributeNum - 2];
			for (int j = 0; j < trainData.attributeNum - 2; j++){
				input[j] = trainData.dataset.get(index)[j+2];
			}
			network.forword(input);
			double[] output = network.getOutputFromNumber(trainData.dataset.get(index)[1]);
			network.backpropagation(output);
	//		network.printNetwork();
		}
		System.out.println(trainData.calMisRecord(network));
		return network;		
	}
	
	public static Network PSOBPApproach(Data trainData, Data testData){
		System.out.println("PSO-BP Approach!!!");

		
		Random seed = new Random();
		ParticleSwarm particleSwarm = new ParticleSwarm(trainData, 10, 8, 17, 1);
		while (!particleSwarm.isIteratoinEnd()){
			particleSwarm.update();
		}
		Network network = particleSwarm.bestPosition;
		network.setClassNumber(2);
		System.out.println(trainData.calMisRecord(network));
		network.setLearningRate(learnRate);
		
		network.printNetwork();
		System.out.println(trainData.calMisRecord(network));
		for (int i = 0; i < trainTimes; i++){
			if ((i % 1000 == 0)){
				System.out.println(trainData.calMisRecord(network));
			}
			network.setLearningRate(Math.pow(Math.E, (double)i / trainTimes * -1) * learnRate);
			int index = Math.abs(seed.nextInt() % trainData.dataset.size());
		//	int index = i % trainData.dataset.size();
			double[] input = new double[trainData.attributeNum - 2];
			for (int j = 0; j < trainData.attributeNum - 2; j++){
				input[j] = trainData.dataset.get(index)[j+2];
			}
			network.forword(input);
			double[] output = network.getOutputFromNumber(trainData.dataset.get(index)[1]);
			network.backpropagation(output);
	//		network.printNetwork();
		}
		System.out.println(trainData.calMisRecord(network));
		network.printNetwork();
		return network;
	}
	
	public static Network SABPApproach(Data trainData, Data testData){
	    double MIN_E=2;
	    boolean is_sa=false;
//	    double K=1.3806505*100*1.2;
	    double sa_reduce=0.93;
	    boolean using_sa=true;
	    
	    double train_accu=0.0;
        double history_accu=1.0;
        
        double lastE=0.0;
        double curE=0.0;
        double historyE=0.0;
        double tempE=0.0; //记录局部最优点
        double saT=100;//退火初始温度
        int sa_num=0;
        double saT_min=10;//退火最低温度
        int sa_flag=0;
	    
        Network network = new Network(4, 9, 1);
        network.setLearningRate(0.9);
        network.initSAMAtrix();
        network.printNetwork();
        
        Normalizer normalizer = new Normalizer();
    //  normalizer.normalizeByMinMax(trainData.dataset, true);
        normalizer.normalizeByZScore(trainData.dataset, true);
    //  normalizer.normalizeByLogistic(trainData.dataset, true);
        System.out.println("ori accu: "+trainData.calMisRecord(network));
        
        trainData.genSaRandomArray();
            
        int iteration=0;
        int flag_ite=0;
        double lastaccu=0.0;
        double train_accu_criteria=0.0;
        Random seed = new Random();
        
        while(true)
        {
            if(is_sa) {//sa mode
                network.randomChangeWeight();              
                curE=trainData.calSaError(network);
                train_accu = trainData.calMisRecord(network);
                if(train_accu>0.92){
                    System.out.println("reached 0.92");
                    is_sa=false;
                    break;
                }
                
                if(train_accu-history_accu>0.1||tempE-curE>100)
                {
                    is_sa=false;
                    saT=100;
                    saT_min=10;
	                System.out.println("退出退火时的准确率为 "+trainData.calMisRecord(network));
                }
                
                if(saT>saT_min){                
                    if(curE<tempE){                       
                        tempE=curE;
                        network.assignWeightTemp(0);//保存当前weight值到temp中        
                    }
                    else if(curE>tempE){
                        double P = Math.exp((tempE-curE)/saT);
                        if(P>new Random().nextDouble()){
                            tempE=curE;
                            network.assignWeightTemp(0);
                        }
                        else{
                            network.assignWeightTemp(1);
                        }
                        //saT=saT/(1+Math.log(sa_num+100)/Math.log(Math.E));         
                        saT=saT*sa_reduce;
                        sa_num++;
                    }
                    
                }
                else {
                    network.assignWeightTemp(1);
                    System.out.println("退出退火时的准确率为"+trainData.calMisRecord(network));
                    is_sa=false;
                    saT=100;
                    saT_min=10;
                }
                continue;
            }         
            else{//bp 模式
                System.out.println("第"+iteration+"轮进入bp时的准确率为"+trainData.calMisRecord(network));
                for (int i = 0; i < trainData.dataset.size(); i++){
                    
                    int index =0;
                    index = i%trainData.dataset.size();
//	                  index= Math.abs(seed.nextInt()) % trainData.dataset.size();
//	                  network.setLearningRate(Math.pow(Math.E, (double)i / trainTimes * -1) * learnRate);
                    double[] input = new double[trainData.attributeNum - 2];
                    for (int j = 0; j < trainData.attributeNum - 2; j++){
                        input[j] = trainData.dataset.get(index)[j+2];
                    }
                    network.forword(input);
                    
                    double[] output = network.getOutputFromNumber(trainData.dataset.get(index)[1]);
                    network.backpropagation(output);
                }
                
                lastE=curE;
                curE=trainData.calSaError(network);
                double deltaE=curE-lastE;
                lastaccu=train_accu_criteria;
                train_accu_criteria=trainData.calMisRecord(network); 
                System.out.println("第"+iteration+"轮退出bp时的准确率为"+train_accu_criteria);
                
                //判断是否应转入退火模式,原则是datasize十分之一的的E(w)
                if(using_sa){
                    if((Math.abs(deltaE)<MIN_E||lastaccu-train_accu_criteria<0.01)&&train_accu_criteria<0.85){
                        sa_flag++;
                        if(sa_flag==3) //连续三次小于某一个值进入退火模式
                        {
                            tempE=curE;
                            historyE=curE; //记录进入时刻的偏差值
                            history_accu=train_accu_criteria;
                            System.out.println("进入退火时的准确率为"+trainData.calMisRecord(network));
                            network.assignWeightTemp(0);
                            is_sa=true;
                            sa_flag=0;
                        }              
                    }
                    else sa_flag=0;
                } 
                iteration++;
            }
            train_accu = trainData.calMisRecord(network);
            if(train_accu>0.90)
            {
                flag_ite++;
                if(flag_ite==1){
                    break;
                }                  
            }
      }    
	                            
	      train_accu = trainData.calMisRecord(network);
	      System.out.println("final training accu: "+train_accu+", iteration: "+iteration);

	  //  normalizer.normalizeByMinMax(testData.dataset, false);
	      normalizer.normalizeByZScore(testData.dataset, false);
	  //  normalizer.normalizeByLogistic(testData.dataset, false);
	      System.out.println("test accu: "+testData.calMisRecord(network));
	      return network;
	}
	    
}
